package com.qianfeng.service.impl;

import com.qianfeng.Event.LoginEvent;
import com.qianfeng.Event.LoginFailEvent;
import com.qianfeng.dto.userDto;
import com.qianfeng.exception.AddDataException;
import com.qianfeng.exception.FindDataException;
import com.qianfeng.mapper.UserMapper;
import com.qianfeng.pojo.SysUserInfo;
import com.qianfeng.service.UserService;
import com.qianfeng.status.ResultCode;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.List;

/*
 *@Description:
 *@author:刘哥
 *@Date:86180 2022/9/5 16:05
 */
@Service
//开启事务的注解,此类所有方法都开启事务,属性都是默认的
@Transactional
public class UserServiceImpl implements UserService {
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    @Autowired
    public void setbCryptPasswordEncoder(BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    private ApplicationContext context;
    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    private UserMapper userMapper;

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public void addUser(userDto userDto) {
//      通过注解校验过了,此处就不需要通过断言校验了
//        Assert.isTrue(!userDto.isEmpty(),()->{
//            throw new AddDataException("参数为空", ResultCode.PARAM_CONTENT_EMPTY);
//        });
//        通过唯一的数据对用户进行查询，判断是否可以注册
        List<SysUserInfo> sysUserInfos=userMapper.findByUserNamePasswordEmail(userDto.getUsername(),userDto.getPhone(),userDto.getEmail());
        Assert.isTrue(sysUserInfos.size()==0,()->{
            throw new AddDataException( "账号已经存在",ResultCode.USERNAME_ALREADY_EXIST);
        });
        SysUserInfo sysUserInfo=new SysUserInfo();

// 校验数据不为空后,将给定源bean的属性值复制到给定目标bean中。(将userDto的值给sysUserInfo)
        BeanUtils.copyProperties(userDto,sysUserInfo);
//        将密码加密存到数据库
        //将密码转成散列值,比如MD5
        //MD5是相同的内容总是会有相同的MD5值,所以为了避免相同的密码在数据库中显示相同的内容,我们会对数据进行加盐
        //盐不能一样,因为盐如果一样和一样的密码就没有区别了,盐是我们随机生成的
        //我们随机生成一个十位的字符串
//        String salt = MyStringUtils.createRandomString(10);
//        //盐必须要保存,否则下次得到的结果不一样的话就无法校验数据了
//        sysUserInfo.setPwdSalt(sysUserInfo.getUsername());
////        用spring提供的md5方法加密
//        String passwordMd5 = DigestUtils.md5DigestAsHex((sysUserInfo.getUsername() + sysUserInfo.getPassword()).getBytes(StandardCharsets.UTF_8));
////        将传过来的密码替换成md5加密过的密码
//        sysUserInfo.setPassword(passwordMd5);

//        使用bCryptPasswordEncoder编码器加密
        sysUserInfo.setPassword(bCryptPasswordEncoder.encode(sysUserInfo.getPassword()));
        //自己注册的就是自己,管理员添加的就写管理员的账号
        sysUserInfo.setCreateBy(sysUserInfo.getUsername());
        sysUserInfo.setRemark("普通用户");
        userMapper.addUser(sysUserInfo);

    }
    @Override
    public SysUserInfo findByUserName(String userName) {
//        Assert.hasText(userName,()->{
//            throw new FindDataException("用户名不存在",ResultCode.USERNAME_ALREADY_EXIST);
//        });
        SysUserInfo sysUserInfo=userMapper.findUserByName(userName);
        return sysUserInfo;
    }
    @Override
    public SysUserInfo login(String userName, String password) {
        SysUserInfo sysUserInfo=findByUserName(userName);
        Assert.isTrue(sysUserInfo!=null,()->{
            context.publishEvent(new LoginFailEvent(null,userName));
            throw new FindDataException("该用户不存在,请重新输入或注册",ResultCode.USERNAME_PASSWORD_ERROR);
        });
        System.out.println(sysUserInfo);
        String passwordMd5 = DigestUtils.md5DigestAsHex((password + sysUserInfo.getPwdSalt()).getBytes(StandardCharsets.UTF_8));
        Assert.isTrue(passwordMd5.equals(sysUserInfo.getPassword()),()->{
//            当密码校验完成密码不正确后,使用登录失败事件
            context.publishEvent(new LoginFailEvent(null,userName));
            throw new FindDataException("用户密码错误",ResultCode.USERNAME_PASSWORD_ERROR);
        });
        //更新日志不是当前业务的核心操作,属于辅助操作,并且成功与否不能影响前面的登录操作,所以当前操作可以放在异步中执行
        //怎么搞一个异步?
        //虽然我们的需求中并没有展示登录时间等操作,但是要记住,我们几乎所有和登录,修改等相关的操作,在表中都有对应的时间列
        //我们需要更新用户的数据,登录的时间和ip
//        String ip = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest().getRemoteHost();
//        userMapper.updateLoginDataByUserId(sysUserInfo.,ip);
//       使用事件修改结果
        context.publishEvent(new LoginEvent(sysUserInfo.getUId(),sysUserInfo.getUsername()));
        return sysUserInfo;
    }
//修改登录ip
    @Override
    public void updateLoginData(Long uid, String ip) {
        userMapper.updateLoginDataByUserId(uid,ip);
    }

}
